
#ifndef __TEXIMAGE_TEXIMAGE__HEADER_FILE__
#define __TEXIMAGE_TEXIMAGE__HEADER_FILE__

#include "coid/comm/dynarray.h"
#include "coid/comm/str.h"
#include "coid/comm/binstream/filestream.h"
#include "teximaget.h"

////////////////////////////////////////////////////////////////////////////////
#include "SDL.h"


////////////////////////////////////////////////////////////////////////////////
struct TexImage
{
    static bool sw_surfaces;

    opcd create( uint w, uint h, uint bpp, bool sw )
    {
        DASSERT( w!=0 && h!=0 );

        _bpp = bpp;
        _rect.w = (uint16)w;
        _rect.h = (uint16)h;

        set_drawer();
        _surf = create_surface( w, h, sw );
        _own = true;

        return 0;
    }

    opcd create( SDL_Surface* surf )
    {
        if( surf == 0 )
            return ersINVALID_PARAMS;

        _surf = surf;
        _bpp = surf->format->BitsPerPixel;
        SDL_GetClipRect( surf, &_rect );

        set_drawer();
        _own = false;

        return 0;
    }


    opcd create_from_template( const TexImage& img, uint bpp, bool sw )
    {
        return create( img.width(), img.height(), bpp ? bpp : img._bpp, sw );
    }

    opcd create_from( TexImage& img, uint bpp, bool sw )
    {
        opcd e = create( img.width(), img.height(), bpp ? bpp : img._bpp, sw );
        if(!e)
            draw(img);
        return e;
    }

    void duplicate( TexImage& dst, bool sw )
    {
        opcd e = dst.create( width(), height(), _bpp, sw );
        if(!e)
            dst.draw(*this);
    }


    void set_drawer()
    {
        static DrawerT<32> STdrawer32;
        static DrawerT<24> STdrawer24;
        static DrawerT<15> STdrawer15;
        static DrawerT<16> STdrawer16;

        if( _bpp == 32  ||  _bpp == 8888 )
            _draw = &STdrawer32;
        else if( _bpp == 24  ||  _bpp == 888 )
            _draw = &STdrawer24;
        else if( _bpp == 16  ||  _bpp == 565 )
            _draw = &STdrawer16;
        else if( _bpp == 15  ||  _bpp == 555 )
            _draw = &STdrawer15;
        else
            _draw = 0;
    }

    void destroy()
    {
        if( _own && _surf )
        {
            SDL_FreeSurface(_surf);
        }

        _surf = 0;
        _own = true;
    }


    void* lock()
    {
        if( SDL_MUSTLOCK(_surf) )
            SDL_LockSurface(_surf);
        return (uchar*)_surf->pixels + _rect.y*_surf->pitch + _rect.x*align_to_chunks_pow2((uint)_bpp,3);
    }
    void unlock()
    {
        if( SDL_MUSTLOCK(_surf) )
            SDL_UnlockSurface(_surf);
    }

    void flip()             { SDL_Flip(_surf); }

    uint pitch() const      { return _surf->pitch; }
    int  width() const      { return _rect.w; }
    int  height() const     { return _rect.h; }


    void set_colorkey( bool set, uint32 c, bool rle )
    {
        uint flg = 0;
        if(set)
            flg |= SDL_SRCCOLORKEY;
        if(rle)
            flg |= SDL_RLEACCEL;
        SDL_SetColorKey( _surf, flg, SDL_MapRGB(_surf->format, uchar((c>>16)&0xff), uchar((c>>8)&0xff), uchar(c&0xff) ) );
    }

    void set_alpha( bool set, uint8 alpha, bool rle=true )
    {
        uint flg = 0;
        if(set)
            flg |= SDL_SRCALPHA;
        if(rle)
            flg |= SDL_RLEACCEL;
        SDL_SetAlpha( _surf, flg, alpha );
    }

    void alpha_to_colorkey( uint32 c, uint32 replc );


    opcd readTGA( const char* name );
    opcd readTGA( const char* name, uint32 colpri, uint32 colsec );
    opcd readRAW( const token& name, uint bpp=0 );
    opcd writeRAW( const token& name );


    void clear( uint32 c )
    {
        SDL_FillRect( _surf, 0, SDL_MapRGB( _surf->format, uchar((c>>16)&0xff), uchar((c>>8)&0xff), uchar(c&0xff) ) );
    }


    void fill( uint x, uint y, uint w, uint h, uint32 c )
    {
        SDL_Rect r;
        r.x = x + _rect.x;
        r.y = y + _rect.y;
        r.w = w;
        r.h = h;
        SDL_FillRect( _surf, &r, SDL_MapRGB( _surf->format, uchar((c>>16)&0xff), uchar((c>>8)&0xff), uchar(c&0xff) ) );
    }

    void draw( TexImage& src, int x=0, int y=0, int mode=DRAWMODE_BLIT )
    {
        if( src._rastflg == 0 )
        {
            //straight blit, use sdl
            // sdl does not copy alpha value if both surfaces have it set, override
            bool bothalpha = mode == DRAWMODE_BLIT  &&  (src._surf->flags & SDL_SRCALPHA) && (_surf->flags & SDL_SRCALPHA);
            if(bothalpha)
                SDL_SetAlpha( src._surf, 0, 0xff );

            SDL_Rect rdst;
            rdst.x = x + _rect.x;
            rdst.y = y + _rect.y;
            SDL_BlitSurface( src._surf, &src._rect, _surf, &rdst );

            if(bothalpha)
                SDL_SetAlpha( src._surf, SDL_SRCALPHA, 0xff );
        }
        else if( _bpp != 32  &&  src._bpp == 32 )
            p_draw( src, x, y, mode, &_draw->_draw32 );
        else
            p_draw( src, x, y, mode, &_draw->_draw );
    }

    void draw_repeat( TexImage& src, int x=0, int y=0, int mode=DRAWMODE_BLIT )
    {
        int h = height();
        y = int_umod( y, h );
        
        draw_rx( src, x, y, mode );

        if( y+src.height() > height() )
            draw_rx( src, x, y-height(), mode );
    }

    void draw_all_tiles( TexImage& src, int mode=DRAWMODE_BLIT )
    {
        int sw = src.width();
        int sh = src.height();
        int h = height();

        for( int y=0; h>0; h-=sh, y+=sh )
        {
            int w = width();
            for( int x=0; w>0; w-=sw, x+=sw )
                draw( src, x, y, mode );
        }
    }


    struct ColorizedSprite;

    void draw_colorize( ColorizedSprite& src, TexImage* backg, uint32 colpri, uint32 colsec, int x=0, int y=0 )
    {
        DASSERT( _bpp == 16  ||  _bpp == 15 );

        src.set_colors( colpri, colsec );
        TexImage* tx = src.get_cached(backg);
        if(tx) {
            draw( *tx, x, y );
            return;
        }

        p_draw_colorize( src, x, y );
    }

    void draw_colorize_repeat( ColorizedSprite& src, TexImage* backg, uint32 colpri, uint32 colsec, int x=0, int y=0 )
    {
        src.set_colors( colpri, colsec );
        TexImage* tx = src.get_cached(backg);
        if(tx) {
            draw_repeat( *tx, x, y );
            return;
        }

        int h = height();
        y = int_umod( y, h );

        draw_colorize_rx( src, x, y );

        if( y+src.get_height() > height() )
            draw_colorize_rx( src, x, y-height() );
    }

    
    void draw_colorize( TexImage& src, uint32 colpri, uint32 colsec, int x, int y, int mode=DRAWMODE_BLIT )
    {
        if( _bpp != 32  &&  src._bpp == 32 )
            p_draw_colorize( src, colpri, colsec, x, y, mode, &_draw->_draw32 );
        else
            p_draw_colorize( src, colpri, colsec, x, y, mode, &_draw->_draw );
    }

    void draw_colorize_repeat( TexImage& src, uint32 colpri, uint32 colsec, int x, int y, int mode=DRAWMODE_BLIT )
    {
        int h = height();
        y = int_umod( y, h );

        draw_colorize_rx( src, colpri, colsec, x, y, mode );

        if( y+src.height() > height() )
            draw_colorize_rx( src, colpri, colsec, x, y-height(), mode );
    }

    enum {
        DRAWMODE_BLIT=0,
        DRAWMODE_ALPHAKEY_BLIT,
        DRAWMODE_ALPHA_BLIT,
        DRAWMODE_SHADOW,
    };

    SDL_Surface* _surf;
    SDL_Rect _rect;

    short _bpp;
    short _rastflg;

    bool  _own;

    enum {
        C_SW            = 0,
        C_SE            = 1,
        C_NW            = 2,
        C_NE            = 3,

        xCORNER         = 3,

        fFLIP           = 4,        ///< flip along the diagonal 

        C_SWF           = C_SW | fFLIP,
        C_SEF           = C_SE | fFLIP,
        C_NWF           = C_NW | fFLIP,
        C_NEF           = C_NE | fFLIP,
    };

    TexImage()
    {
        _rect.w = _rect.h = 0;
        _rect.x = _rect.y = 0;
        _surf = 0;
        _bpp = 0;
        _own = true;

        _rastflg = C_SW;

        _draw = 0;
    }

    TexImage( const TexImage& tex, uint rflg = C_SW )
    {
        set( tex, 0, 0, 0, 0, rflg );
    }

    TexImage( const TexImage& tex, uint x, uint y, uint w, uint h, uint rflg = C_SW )
    {
        set( tex, x, y, w, h, rflg );
    }

    ~TexImage()
    {
        destroy();
    }

    void set( const TexImage& tex, uint x, uint y, uint w, uint h, uint rflg = C_SW )
    {
        tex.get( *this, x, y, w, h );

        _rastflg = rflg;
    }

    void set_scanmode( uint flg )
    {
        _rastflg = flg;
    }

    TexImage& get( TexImage& sub, uint x, uint y, uint w, uint h ) const
    {
        if(w==0)  w = _rect.w - x;
        if(h==0)  h = _rect.h - y;

        DASSERT( x>=0 && x+w<=_rect.w );
        DASSERT( y>=0 && y+h<=_rect.h );

        sub._surf = _surf;
        sub._rect.x = _rect.x+x;
        sub._rect.y = _rect.y+y;
        sub._rect.w = w;
        sub._rect.h = h;

        sub._own = false;

        sub._bpp = _bpp;
        sub._draw = _draw;

        return sub;
    }

    void flip_to_alpha( uint32 col, bool invert, uint mulalpha=0x100 );
    void copy_alpha( TexImage& alpha );

    void invert();
    void colorize_image( TexImage& bitmap, uint32 colorpri, uint32 colorsec );

    void convolute( TexImage& msk );
    void reduce_alpha( float mul );
    void make_transparent( uint32 col );
    void make_blue_transparent();

    
    opcd compose( const token& path, const token& suffix, uint ndigits, uint intfrom, uint nx, uint ny )
    {
        charstr mm = path;
        uint lmm = mm.len();

        int sx=0, sy=0;

        TexImage tmp;
        
        for( uint py=0; py<ny; ++py )
        {
            for( uint px=0; px<nx; ++px )
            {
                mm.trim_to_length(lmm);
                mm.append_num( 10, intfrom++, ndigits, charstr::ALIGN_NUM_FILL_WITH_ZEROS );
                mm << suffix;

                if( 0 != tmp.readTGA(mm.ptr()) )  return ersIO_ERROR;

                if( sx == 0 )
                {
                    sx = tmp.width();
                    sy = tmp.height();

                    create( nx*sx, ny*sy, tmp._bpp, true );
                }

                draw( tmp, px*sx, py*sy );
            }
        }

        return 0;
    }


    ////////////////////////////////////////////////////////////////////////////////
    struct ColorizedSprite
    {
        void draw_line( uint line, ushort* dst, int len, int skip ) const
        {
            if( _rflg & fFLIP )
                line = _rect.h - 1 - line;
            const uchar* src = _data[line+_rect.y].ptr();

            uchar k = skip_line_colorize( src, skip+_rect.x );
            draw_line_colorize( k, src, dst, len );
        }

        int get_width() const       { return _rect.w; }
        int get_height() const      { return _rect.h; }

        void set_colors( uint32 colpri, uint32 colsec )
        {
            _ck.colpri = _bpp == 15  ?  get_color555(colpri) : get_color565(colpri);
            _ck.colsec = _bpp == 15  ?  get_color555(colsec) : get_color565(colsec);
        }

        void create_from32( uint bpp, TexImage& img, TexImage* backg = 0 );

        void set( const ColorizedSprite& src, uint x, uint y, uint w=0, uint h=0, uint rflg = C_SW )
        {
            src.get( *this, x, y, w, h );

            _rflg = rflg;
            _ck.that = this;
        }

        ColorizedSprite& get( ColorizedSprite& sub, uint x, uint y, uint w, uint h ) const
        {
            if(w==0)  w = _rect.w - x;
            if(h==0)  h = _rect.h - y;

            DASSERT( x>=0 && x+w<=_rect.w );
            DASSERT( y>=0 && y+h<=_rect.h );

            sub._data = _data;
            sub._rect.x = _rect.x+x;
            sub._rect.y = _rect.y+y;
            sub._rect.w = w;
            sub._rect.h = h;

            sub._owndata.reset();

            sub._bpp = _bpp;

            return sub;
        }


        ColorizedSprite()
        {
            _data = 0;
            _rect.w = _rect.h = 0;

            _bpp = 0;
            _rflg = 0;

            _ck.that = this;
        }

        ColorizedSprite( const ColorizedSprite& tex, uint rflg = C_SW )
        {
            set( tex, 0, 0, 0, 0, rflg );
        }

        ColorizedSprite( const ColorizedSprite& tex, uint x, uint y, uint w, uint h, uint rflg = C_SW )
        {
            set( tex, x, y, w, h, rflg );
        }


        void save( binstream& bin ) const
        {
            DASSERT( _owndata.size() > 0 );
            bin << _bpp << _rect.w << _owndata;
        }

        void load( binstream& bin, uint bpp )
        {
            bin >> _bpp >> _rect.w >> _owndata;

            _data = _owndata.ptr();

            _rect.h = _owndata.size();
            _rect.x = 0;
            _rect.y = 0;

            _rflg = 0;

            if( bpp != _bpp )
                translate_bpp(bpp);
        }

        struct ColorizedKey
        {
            uint16 colpri,colsec;
            ColorizedSprite* that;

            bool operator == (const ColorizedKey& ck) const
            {   return that==ck.that && colpri == ck.colpri && colsec == ck.colsec; }

            uint32 suggested_transparent( uint bpp ) const
            {
                uint32 cp = bpp==15 ? get_color32_555(colpri) : get_color32_565(colpri);
                uint32 cs = bpp==15 ? get_color32_555(colsec) : get_color32_565(colsec);
                cp &= 0x00808080;
                cs &= 0x00808080;
                cp = ((cp>>21) | (cp>>14) | (cp>>7)) & 0x07;
                cs = ((cs>>21) | (cs>>14) | (cs>>7)) & 0x07;
                uint i;
                for( i=1; i<7; ++i )
                    if( cp!=i && cs!=i ) break;
                uint32 c;
                if(i&4) c |= 0x00ff0000;
                if(i&2) c |= 0x0000ff00;
                if(i&1) c |= 0x000000ff;
                return c;
                //return bpp==15 ? get_color555(c) : get_color565(c);
            }
        };

        TexImage* get_cached( TexImage* backg );

    protected:


        void translate_bpp( uint bpp )
        {
            for( uint i=0; i<_owndata.size(); ++i )
            {
                const uchar* src = _owndata[i].ptr();
                const uchar* sre = src + _owndata[i].size();

                for( ; src<sre; )
                {
                    uchar k = *src++;
                    uchar n = k >> 2;

                    if( (k&xSTATE) == STATE_COPY )
                    {
                        const uchar* srcc = src + n*sizeof(ushort);
                        for( ; src<srcc; src+=2 )
                            convert_bpp( *(ushort*)src, bpp );
                    }
                    else if( k & fSTATE_COL )
                    {
                        const uchar* srcc = src + n*2*sizeof(ushort);
                        for( ; src<srcc; src+=4 )
                            convert_bpp( *(ushort*)src, bpp );
                    }
                }
            }

            _bpp = bpp;
        }

        static void convert_bpp( ushort& col, uint bpp )
        {
            if( bpp == 15 )
                col = (col&0x001f) | ((col>>1)&0x7fe0);
            else
                col = (col&0x001f) | ((col<<1)&0xffe0);
        }

        typedef dynarray<uchar> t_line;

        static void check_state( int& stold, int st, t_line& row, uint& offs )
        {
            if( stold != -1 )
            {
                stold += xSTATE + 1;
                if( (stold & xSTATE) == st
                    &&  (stold >> rSTATE_) < (1<<6)-1 )  return;

                row[offs] = (uchar)stold;
            }

            offs = row.size();
            *row.add() = STATE_SKIP;

            stold = st;
        }

        void add_coldata( int st, uint32 c, t_line& row )
        {
            if( (st&xSTATE) == STATE_COPY )
            {
                *(uint16*)row.add(2) = _bpp == 15  ?  get_color555(c) : get_color565(c);
                return;
            }

            uint16* dst = (uint16*)row.add(4);

            const uchar* pb = (const uchar*)&c;
            dst[0] = get_brightness(pb);
            dst[1] = (st&xSTATE) == STATE_COLPRI  ?  pb[1] - pb[0]  :  pb[2] - pb[0];
            ++dst[1];
            dst[1] >>= 3;   //to 5-bit format
        }

        uint16 get_brightness( const uchar* pb ) const
        {
            uint16 k = pb[0] >> 3;
            if( _bpp == 15 )
                return k | (k<<5) | (k<<10);
            else
                return k | (k<<6) | (k<<11);
        }

        enum {
            rSTATE_             = 2,
            xSTATE              = (1<<rSTATE_)-1,
            STATE_SKIP          = 0,
            STATE_COPY          = 1,
            STATE_COLPRI        = 2,
            STATE_COLSEC        = 3,
        
            fSTATE_COL          = 2,
        };

        void draw_line_colorize( uchar k, const uchar* src, ushort* dst, int len ) const
        {
            for( ; len>0; )
            {
                //uchar k = *src++;
                uchar n = k >> 2;
                if(n>len)  n = len;

                if( (k&xSTATE) == STATE_COPY )
                {
                    ushort* pd = (ushort*)src;
        
                    xmemcpy( dst, pd, n*sizeof(ushort) );
                    src += n*sizeof(ushort);
                }
                else if( k & fSTATE_COL )
                {
                    ushort col;
                    if( (k&xSTATE) == STATE_COLSEC )
                        col = _ck.colsec;
                    else
                        col = _ck.colpri;
            
                    const ushort* pd = (const ushort*)src;

                    if( _bpp == 15 )
                    {
                        for( uchar i=0; i<n; ++i )
                        {
                            dst[i] = pd[0] + scale_packed555( pd[1], col );
                            pd += 2;
                        }
                    }
                    else
                    {
                        for( uchar i=0; i<n; ++i )
                        {
                            dst[i] = pd[0] + scale_packed565( pd[1], col );
                            pd += 2;
                        }
                    }
                    src = (const uchar*)pd;
                }

                dst += n;
                len -= n;

                k = *src++;
            }
        }

        static uchar skip_line_colorize( const uchar*& src, int len )
        {
            if( len == 0 )
            {
                uchar k = *src++;
                return k;
            }

            for(;;)
            {
                uchar k = *src++;
                uchar n = k >> 2;
                if(n>len)  n = len;

                if( (k&xSTATE) == STATE_COPY )
                    src += n*sizeof(ushort);
                else if( k & fSTATE_COL )
                    src += n*2*sizeof(ushort);

                len -= n;
                if( len == 0 )
                {
                    k = k - (n<<2);
                    if( (k>>2) == 0 )
                        k = *src++;
                    return k;
                }
            }
        }

        static inline uint16 scale_packed555( uint32 scale, uint16 p )
        {
            uint32 g = p & 0x03e0;
            uint32 rb = p & 0x7c1f;

            g *= scale;
            rb *= scale;

            rb >>= 5;
            g >>= 5;
            rb &= 0x7c1f;
            g &= 0x03e0;

            return uint16(rb | g);
        }

        static inline uint16 scale_packed565( uint32 scale, uint16 p )
        {
            uint32 g = p & 0x07e0;
            uint32 rb = p & 0xf81f;

            g *= scale;
            rb *= scale;

            rb >>= 5;
            g >>= 5;
            rb &= 0xf81f;
            g &= 0x07e0;

            return uint16(rb | g);
        }

    private:
        ColorizedKey _ck;
        //uint16 _colpri;
        //uint16 _colsec;

    //packed sprite format
    // type: skip,red,green
    // count
    // data: u16 brightness555, u16 delta (0..32)

        const t_line* _data;
        SDL_Rect _rect;

        dynarray<t_line> _owndata;
        ushort _bpp;
        ushort _rflg;
    };


    ////////////////////////////////////////////////////////////////////////////////
    struct ColorizedSpriteArray
    {
        dynarray<ColorizedSprite>   _array;


        opcd load( const token& path, uint bpp );
        opcd save( const token& path );

        opcd compose( uint bpp, const token& path, const token& suffix, uint ndigits, uint intfrom, uint n, TexImage* backg = 0 )
        {
            charstr mm = path;
            uint lmm = mm.len();

            TexImage tmp;
            _array.need_new(n);
        
            for( uint i=0; i<n; ++i )
            {
                mm.trim_to_length(lmm);
                mm.append_num( 10, intfrom++, ndigits, charstr::ALIGN_NUM_FILL_WITH_ZEROS );
                mm << suffix;

                if( 0 != tmp.readTGA(mm.ptr()) )  return ersIO_ERROR;
                tmp.make_blue_transparent();

                _array[i].create_from32( bpp, tmp, backg );
            }

            return 0;
        }


        opcd compose2way( uint bpp, const token& path, const token& suffix, uint ndigits, uint intfrom, uint n, TexImage* backg=0, uint ntx=1, uint nty=1 )
        {
            charstr mm = path;
            uint lmm = mm.len();

            TexImage tmp, tmpd;
            ColorizedSprite* pcs = _array.need_new(n*2*ntx*nty);
        
            for( uint i=0; i<n; ++i )
            {
                mm.trim_to_length(lmm);
                mm.append_num( 10, intfrom++, ndigits, charstr::ALIGN_NUM_FILL_WITH_ZEROS );
                mm << suffix;

                if( 0 != tmp.readTGA(mm.ptr()) )  return ersIO_ERROR;
                
                int dx = tmp.width() / ntx;
                int dy = tmp.height() / nty;

                if( ntx == 1 && nty == 1 )
                {
                    create2way( pcs, bpp, tmp, backg );
                    pcs += 2;
                }
                else
                {
                    for( uint iy=0; iy<nty; ++iy )
                    {
                        for( uint ix=0; ix<ntx; ++ix, pcs+=2 )
                        {
                            TexImage t( tmp, ix*dx, iy*dy, dx, dy );
                            create2way( pcs, bpp, t, backg );
                        }
                    }
                }
            }

            return 0;
        }

        opcd compose2way_spec( uint bpp, const token& path, const token& suffix, uint ndigits, uint intfrom, uint n, TexImage* backg=0, uint nty=1 )
        {
            charstr mm = path;
            uint lmm = mm.len();

            TexImage tmp, tmpd;
            ColorizedSprite* pcs = _array.need_new(n*2*(nty+1));
        
            for( uint i=0; i<n; ++i )
            {
                mm.trim_to_length(lmm);
                mm.append_num( 10, intfrom++, ndigits, charstr::ALIGN_NUM_FILL_WITH_ZEROS );
                mm << suffix;

                if( 0 != tmp.readTGA(mm.ptr()) )  return ersIO_ERROR;
                
                int dy = tmp.height() / nty;

                for( uint iy=0; iy<nty; ++iy, pcs+=2 )
                {
                    TexImage t( tmp, 0, iy*dy, tmp.width(), dy );
                    if( iy+1 == nty )
                        create4way( pcs, bpp, t, backg );
                    else
                        create2way( pcs, bpp, t, backg );
                }
                pcs += 2;
            }

            return 0;
        }


        opcd compose4way( uint bpp, const token& path, const token& suffix, uint ndigits, uint intfrom, uint n, TexImage* backg=0, uint ntx=1, uint nty=1 )
        {
            charstr mm = path;
            uint lmm = mm.len();

            TexImage tmp;
            ColorizedSprite* pcs = _array.need_new( n*4*ntx*nty );
        
            for( uint i=0; i<n; ++i )
            {
                mm.trim_to_length(lmm);
                mm.append_num( 10, intfrom++, ndigits, charstr::ALIGN_NUM_FILL_WITH_ZEROS );
                mm << suffix;

                if( 0 != tmp.readTGA(mm.ptr()) )  return ersIO_ERROR;

                int dx = tmp.width() / ntx;
                int dy = tmp.height() / nty;

                if( ntx == 1 && nty == 1 )
                {
                    create4way( pcs, bpp, tmp, backg );
                    pcs += 4;
                }
                else
                {
                    for( uint iy=0; iy<nty; ++iy )
                    {
                        for( uint ix=0; ix<ntx; ++ix, pcs+=4 )
                        {
                            TexImage t( tmp, ix*dx, iy*dy, dx, dy );
                            create4way( pcs, bpp, t, backg );
                        }
                    }
                }
            }

            return 0;
        }

    protected:
        void create2way( ColorizedSprite* spra, uint bpp, TexImage& tmp, TexImage* backg )
        {
            TexImage tmpd;
            tmpd.create( tmp.height(), tmp.width(), 32, true );

            spra[0].create_from32( bpp, tmp, backg );

            tmp.set_scanmode(C_SEF);
            tmpd.draw(tmp);
            spra[1].create_from32( bpp, tmpd, backg );
        }

        void create4way( ColorizedSprite* spra, uint bpp, TexImage& tmp, TexImage* backg )
        {
            TexImage tmpd;
            tmpd.create( tmp.height(), tmp.width(), 32, true );

            spra[0].create_from32( bpp, tmp, backg );

            tmp.set_scanmode(C_SEF);
            tmpd.draw(tmp);
            spra[1].create_from32( bpp, tmpd, backg );

            tmpd.set_scanmode(C_SEF);
            tmp.draw(tmpd);
            spra[2].create_from32( bpp, tmp, backg );

            tmp.set_scanmode(C_SEF);
            tmpd.draw(tmp);
            spra[3].create_from32( bpp, tmpd, backg );
        }
    };



protected:

    Drawer* _draw;


    void p_draw( TexImage& src, int x, int y, int mode, Drawer::LineDrawers* drw )
    {
        if( mode == DRAWMODE_BLIT )
            draw_tile( x, y, src, drw->_draw_line );
        else if( mode == DRAWMODE_ALPHAKEY_BLIT )
            draw_tile( x, y, src, drw->_draw_line_ak );
        else if( mode == DRAWMODE_ALPHA_BLIT )
            draw_tile( x, y, src, drw->_draw_line_a );
        else if( mode == DRAWMODE_SHADOW )
            draw_tile( x, y, src, drw->_draw_line_s );
    }

    void p_draw_colorize( TexImage& src, uint32 colpri, uint32 colsec, int x, int y, int mode, Drawer::LineDrawers* drw )
    {
        uint32 col[2];
        col[0] = colpri;
        col[1] = colsec;

        if( mode == DRAWMODE_BLIT )
            draw_tile( x, y, src, drw->_draw_line_c, &col );
        else if( mode == DRAWMODE_ALPHAKEY_BLIT )
            draw_tile( x, y, src, drw->_draw_line_cak, &col );
        else if( mode == DRAWMODE_ALPHA_BLIT )
            draw_tile( x, y, src, drw->_draw_line_ca, &col );
    }

    void p_draw_colorize( const ColorizedSprite& src, int x, int y )
    {
        int sw = src.get_width();
        int sh = src.get_height();
        if( x >= width()  ||  x + sw <= 0 )  return;
        if( y >= height()  ||  y + sh <= 0 )  return;

        int skip=0,line=0;
        if( x<0 )  skip -= x, sw += x, x=0;
        if( y<0 )  line -= y, sh += y, y=0;
        if( x+sw > width() )  sw = width()-x;
        if( y+sh > height() )  sh = height()-y;

        int bypp = align_to_chunks_pow2((uint)_bpp,3);
        //uint32* dstd = _data + (_height-1-y)*_width + x;
        uchar* dstd = (uchar*)lock() + x*bypp + y*_surf->pitch;

        for( ; sh>0; --sh )
        {
            src.draw_line( line, (ushort*)dstd, sw, skip );
        
            ++line;
            dstd += _surf->pitch;
        }

        unlock();
    }


    void draw_rx( TexImage& src, int x, int y, int mode )
    {
        int w = width();
        x = int_umod( x, w );

        draw( src, x, y, mode );
        
        if( x+src.width() > w )
            draw( src, x-w, y, mode );
    }

    void draw_colorize_rx( const ColorizedSprite& src, int x, int y )
    {
        int w = width();
        x = int_umod( x, w );

        p_draw_colorize( src, x, y );
        
        if( x+src.get_width() > w )
            p_draw_colorize( src, x-w, y );
    }
  
    void draw_colorize_rx( TexImage& src, uint32 colpri, uint32 colsec, int x, int y, int mode )
    {
        int w = width();
        x = int_umod( x, w );

        draw_colorize( src, colpri, colsec, x, y, mode );
        if( x+src.width() > w )
            draw_colorize( src, colpri, colsec, x-w, y, mode );
    }

    static int _glfwReadTGA( FILE *f, TexImage* img, int flags );


    void draw_tile( int x, int y, TexImage& src, Drawer::fnc_draw_line fnc_dl, void* ctx=0 )
    {
        int w = width();
        int h = height();
        int sw = src.width();
        int sh = src.height();
        if( src._rastflg & TexImage::fFLIP )
            sw = sh, sh = src.width();

        if( x >= w  ||  x + sw <= 0 )  return;
        if( y >= h  ||  y + sh <= 0 )  return;

        const uchar* srcd = (const uchar*)src.lock();

        int bypps = align_to_chunks_pow2((uint)src._bpp,3);
        int xstep = bypps;
        int ystep = src._surf->pitch;

        uint c = src._rastflg & TexImage::xCORNER;
    
        if( c == TexImage::C_NE  ||  c == TexImage::C_NW )
        {
            srcd += (src.height()-1) * src._surf->pitch;
            ystep = -ystep;
        }

        if( c == TexImage::C_SE  ||  c == TexImage::C_NE )
        {
            srcd += (src.width()-1)*bypps;
            xstep = -xstep;
        }

        if( src._rastflg & TexImage::fFLIP )
        {
            int t = xstep;
            xstep = ystep;
            ystep = t;
        }

        if( x<0 )  srcd -= x*xstep, sw += x, x=0;
        if( y<0 )  srcd -= y*ystep, sh += y, y=0;
        if( x+sw > w )  sw = w-x;
        if( y+sh > h )  sh = h-y;

        //uint32* dstd = _data + (_height-1-y)*_width + x;
        uchar* dstd = (uchar*)lock() + x*align_to_chunks_pow2((uint)_bpp,3) + y*_surf->pitch;
        for( ; sh>0; --sh )
        {
            fnc_dl( srcd, dstd, sw, xstep, ctx );
        
            srcd += ystep;
            dstd += _surf->pitch;
        }

        unlock();
        src.unlock();
    }


    SDL_Surface* create_surface( uint w, uint h, bool sw, SDL_Surface* surf=0, int x=0, int y=0 );

    static const uint32* get_rgba_masks( uint bpp )
    {
        static uint32 M15[] = { 0x7c00, 0x03e0, 0x001f, 0x0000 };
        static uint32 M16[] = { 0xf800, 0x07e0, 0x001f, 0x0000 };
        static uint32 M24[] = { 0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000 };
        static uint32 M32[] = { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 };

        switch(bpp)
        {
        case 15:
        case 555:
            return M15;

        case 16:
        case 565:
            return M16;

        case 24:
        case 888:
            return M24;

        case 32:
        case 8888:
            return M32;
        }

        return 0;
    }
};

////////////////////////////////////////////////////////////////////////////////
inline opcd TexImage::ColorizedSpriteArray::load( const token& path, uint bpp )
{
    bifstream bin(path);

    if(!bin.is_open())
        return ersNOT_FOUND;

    uint n;
    bin >> n;
    _array.need_new(n);

    for( uint i=0; i<n; ++i )
        _array[i].load(bin,bpp);

    return 0;
}

inline opcd TexImage::ColorizedSpriteArray::save( const token& path )
{
    bofstream bin(path);

    if(!bin.is_open())
        return ersNOT_FOUND;

    uint n = _array.size();
    bin << n;

    for( uint i=0; i<n; ++i )
        _array[i].save(bin);

    return 0;
}


////////////////////////////////////////////////////////////////////////////////
inline void TexImage::flip_to_alpha( uint32 col, bool invert, uint mulalpha )
{
    RASSERT( _bpp == 32 );

    uchar* dst = (uchar*)lock();
    int h = height();
    int w = width();
	for( int y=0; y<h; ++y )
    {
        for( int x=0; x<w; ++x, dst+=4 )
        {
            uint alpha = dst[0];
            if(invert) alpha = 255-alpha;
            
            alpha *= mulalpha;
            alpha >>= 8;

            if(alpha) *(uint32*)dst = col;
            dst[3] = alpha;
        }
	}

    unlock();
}

////////////////////////////////////////////////////////////////////////////////
inline void TexImage::copy_alpha( TexImage& alpha )
{
    RASSERT( _bpp == 32 );
    RASSERT( alpha.width() == width()  &&  alpha.height() == height() );

    const uchar* src = (const uchar*)alpha.lock();

    uchar* dst = (uchar*)lock() + 3;
    int h = height();
    int w = width();
	for( int y=0; y<h; ++y )
    {
        for( int x=0; x<w; ++x, dst+=4, src+=4 )
            *dst = *src;
	}
}

////////////////////////////////////////////////////////////////////////////////
inline void TexImage::invert()
{
    RASSERT( _bpp == 32 );

    uchar* dst = (uchar*)lock();
    int h = height();
    int w = width();
	for( int y=0; y<h; ++y )
    {
        for( int x=0; x<w; ++x, dst+=4 )
        {
            dst[0] = ~dst[0];
            dst[1] = ~dst[1];
            dst[2] = ~dst[2];
        }
	}

    unlock();
}

////////////////////////////////////////////////////////////////////////////////
inline void TexImage::colorize_image( TexImage& bitmap, uint32 colorpri, uint32 colorsec )
{
    RASSERT( bitmap._bpp == 32 );
    RASSERT( 0 == create_from_template(bitmap,bitmap._bpp,true) );

	int cpr = (colorpri >> 16) & 0xff;
	int cpg = (colorpri >> 8) & 0xff;
	int cpb = (colorpri >> 0) & 0xff;
	int csr = (colorsec >> 16) & 0xff;
	int csg = (colorsec >> 8) & 0xff;
	int csb = (colorsec >> 0) & 0xff;

	//ushort *dbmpline = (*dst)->line[0];

    const uchar* src = (const uchar*)bitmap.lock();
    uchar* dst = (uchar*)lock();
    int h = height();
    int w = width();
	for( int y=0; y<h; ++y )
    {
        for( int x=0; x<w; ++x, dst+=4, src+=4 )
        {
            int dg = src[1] - src[2];
            int dr = src[0] - src[2];
            if( dg >= dr )
            {
                dst[0] = src[2] + ((cpr*dg)>>8);
                dst[1] = src[2] + ((cpg*dg)>>8);
                dst[2] = src[2] + ((cpb*dg)>>8);
            }
            else if(dr)
            {
                dst[0] = src[2] + ((csr*dr)>>8);
                dst[1] = src[2] + ((csg*dr)>>8);
                dst[2] = src[2] + ((csb*dr)>>8);
            }

            dst[3] = src[3];
		}
	}

    unlock();
    bitmap.unlock();
}

////////////////////////////////////////////////////////////////////////////////
///Make pixels with \a col color transparent
inline void TexImage::make_transparent( uint32 col )
{
    RASSERT( _bpp == 32 );

    uint32* dst = (uint32*)lock();
    for( uint i=width()*height(); i>0; --i,++dst )
    {
        if( *dst == col )
            *dst &= 0x00ffffff;
    }

    unlock();
}

////////////////////////////////////////////////////////////////////////////////
///Make bluer pixels transparent
inline void TexImage::make_blue_transparent()
{
    RASSERT( _bpp == 32 );

    uint32* dst = (uint32*)lock();
    for( uint i=width()*height(); i>0; --i,++dst )
    {
        const uchar* p = (const uchar*)dst;
        if( p[0] > p[2] || p[0] > p[1] )
            *dst &= 0x00ffffff;
    }

    unlock();
}

////////////////////////////////////////////////////////////////////////////////
///Make pixels with \a col color transparent
inline void TexImage::alpha_to_colorkey( uint32 c, uint32 replc )
{
    RASSERT( _bpp == 32 );

    c |= 0xff000000;

    uint32* dst = (uint32*)lock();
    for( uint i=width()*height(); i>0; --i,++dst )
    {
        if( (*dst & 0xff000000) == 0 )
            *dst = c;
        else if( *dst == c )
            *dst = replc;
        else
            *dst |= 0xff000000;
    }

    unlock();

    SDL_SetColorKey( _surf, SDL_SRCCOLORKEY | SDL_RLEACCEL, c & 0x00ffffff );
    SDL_SetAlpha( _surf, 0, 0 );
}

////////////////////////////////////////////////////////////////////////////////
///Reduce alpha by coeficient
inline void TexImage::reduce_alpha( float mul )
{
    RASSERT( _bpp == 32 );

    if(mul>1) mul = 1;
    if(mul<0) mul = 0;
    
    int a = int(255*mul);

    uint32* dst = (uint32*)lock();
	for( int i=height()*width(); i>0; --i )
        *dst = scale_packed32( a, *dst );

    unlock();
}

////////////////////////////////////////////////////////////////////////////////
///msk - black: transparent, white: opaque, red
inline void TexImage::convolute( TexImage& msk )
{
    RASSERT( _bpp == 32 );
    RASSERT( width() == msk.width()  &&  height() == msk.height() );

    const uchar* srcl = (const uchar*)msk.lock();
    uchar* dst = (uchar*)lock();

    int h = height();
    int w = width();
	for( int y=0; y<h; ++y,srcl+=msk._surf->pitch )
    {
        const uchar* src = srcl;
        for( int x=0; x<w; ++x, dst+=4, src+=4 )
        {
            if( dst[0] == dst[1]  &&  dst[1] == dst[2] )
            {
                //grey - transparence of source
                uchar d = dst[0];
                dst[3] = d;
                *((uint32*)dst) = scale_packed32_rgb( d, *((uint32*)src) );
            }
            else
            {
                //uchar d = dst[0];
                dst[3] = 127;
                *((uint32*)dst) = scale_packed32( dst[3], *((uint32*)dst) );
            }
		}
	}

    unlock();
    msk.unlock();
}


#endif //__TEXIMAGE_TEXIMAGE__HEADER_FILE__
